package squirrel

import org.json4s._

import scala.util.{Failure, Success, Try}

trait JSONQueryBase {
  val from: JValue
  val where: Option[JObject]
  val attributes: Option[JValue]
  val includes: Option[JArray]
  val offset: Option[Int]
  val limit: Option[Int]

  protected implicit val formats: Formats = DefaultFormats

  def toSqlAst: Try[SqlSelectAst] = Try {
    val from = parseFrom()
    val columns = parseAttributes()
    val w = parseWhere()
    SqlSelectAst(from = from.get,
                 columns = Some(columns.get),
                 where = Some(w.get),
//                   includes = includes,
                 offset = offset,
                 limit = limit)
  }

  protected def parseFrom(): Try[SqlFrom] = Try {
    from match {
      case JString(s) => SqlFromSimple(s)
      case v: JObject =>
        val alias = v.extractOpt[SqlFromAlias]
        if (alias.isDefined) {
          alias.get
        } else {
          val sub = v.extractOpt[JSONSubQuery]
          if (sub.isDefined) {
            val subAst = sub.get.toSqlAst.get
            SqlFromSubQuery(subAst, sub.get.alias)
          } else {
            throw InvalidFromException("invalid from format")
          }
        }
      case _ => throw InvalidFromException("invalid from format")
    }
  }

  protected def parseAttributes(): Try[Seq[SqlColumn]] =
    attributes match {
      case None => Success(Seq(SqlColumnSimple("*")))
      case Some(jValue) =>
        jValue match {
          case JString(s) => Success(Seq(SqlColumnSimple(s)))
          case JArray(arr) =>
            val cols = arr.map(parseColumn)
            cols find (_.isFailure) match {
              case Some(f: Failure[_]) => Failure(f.exception)
              case _                   => Success(cols.map(_.get))
            }
          case _ => Failure(InvalidColumnException("invalid format"))
        }
    }

  private def parseColumn(value: JValue): Try[SqlColumn] = value match {
    case JString(s) => Success(SqlColumnSimple(s))
    case v: JObject => Try(v.extract[SqlColumnAlias])
    case _          => Failure(InvalidColumnException("invalid format"))
  }

  protected def parseWhere(): Try[SqlWhere] = JSONWhere.parse(where)
}
